Skip to content

Conversation

@graycreate
Copy link
Member

Summary

  • Fixed navigation issue where detail pages (FeedDetail, UserDetail, MyFavorite, TagDetail) would immediately close after opening
  • Root cause: Nested navigation containers - NavigationStack at root conflicting with NavigationView inside .navigatable() modifier, plus nested NavigationLinks in cell views
  • Added UI test to verify navigation stays open

Changes

  • Use NavigationStack in MainPage instead of NavigationView
  • Remove .navigatable() calls from detail pages
  • Remove nested NavigationLinks from FeedItemView and RecentItemView
  • Simplify NavbarHostView by removing nested NavigationView

Test plan

  • Build succeeds on iPhone 17 Pro simulator
  • UI test testFeedNavigationStaysOpen passes
  • Manual verification: tap feed item → detail page opens and stays open
  • Test other navigation flows (user profile, favorites, tags)

🤖 Generated with Claude Code

The navigation issue where detail pages would immediately close after
opening was caused by nested navigation containers:

- NavigationStack at root conflicting with NavigationView in .navigatable()
- Nested NavigationLinks inside FeedItemView and RecentItemView
- NavBarModifier wrapping content in additional NavigationView

Changes:
- Use NavigationStack in MainPage instead of NavigationView
- Remove .navigatable() calls from detail pages (FeedDetailPage,
  MyFavoritePage, UserDetailPage, TagDetailPage)
- Remove nested NavigationLinks from FeedItemView and RecentItemView
- Simplify NavbarHostView by removing nested NavigationView
- Add UI test to verify navigation stays open

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Copilot AI review requested due to automatic review settings December 1, 2025 09:51
@github-actions github-actions bot added the size/M label Dec 1, 2025
Copilot finished reviewing on behalf of graycreate December 1, 2025 09:53
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a critical navigation bug where detail pages (FeedDetail, UserDetail, MyFavorite, TagDetail) would immediately dismiss after opening. The root cause was nested navigation containers causing conflicts.

Key changes:

  • Migrated root navigation from NavigationView to NavigationStack in MainPage
  • Removed .navigatable() modifier from detail pages to eliminate nested navigation
  • Removed nested NavigationLink wrappers from cell views (FeedItemView, RecentItemView) that were creating extra navigation layers

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
V2erUITests/V2erUITests.swift Added UI test to verify navigation to detail page stays open after 6 seconds
V2er/View/Widget/NavbarHostView.swift Removed nested NavigationView from NavBarModifier to simplify navigation hierarchy
V2er/View/Tag/TagDetailPage.swift Removed .navigatable() call to prevent nested navigation
V2er/View/Me/UserDetailPage.swift Removed .navigatable() call to prevent nested navigation
V2er/View/Me/MyRecentPage.swift Removed NavigationLink wrapper from avatar and replaced NodeView with plain Text
V2er/View/Me/MyFavoritePage.swift Removed .navigatable() call to prevent nested navigation
V2er/View/MainPage.swift Changed from NavigationView to NavigationStack for iOS 16+ navigation
V2er/View/FeedDetail/FeedDetailPage.swift Removed .navigatable() and nested NavigationLink from avatar; cleaned up commented code
V2er/View/Feed/FeedPage.swift Simplified isSelected property by removing side effect logic
V2er/View/Feed/FeedItemView.swift Removed NavigationLink wrapper from avatar and replaced NodeView with plain Text

Comment on lines 39 to 50
// Wait for feed to load
sleep(6)

// Find a feed item by looking for text that says "评论" (comment count)
let commentLabels = app.staticTexts.matching(NSPredicate(format: "label CONTAINS '评论'"))
guard commentLabels.count > 0 else {
XCTFail("No feed items found (no comment labels)")
return
}

// Tap on the first comment label's parent area
let firstCommentLabel = commentLabels.element(boundBy: 0)
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using sleep() in UI tests is fragile and increases test execution time. Replace with XCTest's waitForExistence(timeout:) method. For example, wait for a feed item to appear: XCTAssertTrue(commentLabels.firstMatch.waitForExistence(timeout: 10), \"Feed should load\").

Suggested change
// Wait for feed to load
sleep(6)
// Find a feed item by looking for text that says "评论" (comment count)
let commentLabels = app.staticTexts.matching(NSPredicate(format: "label CONTAINS '评论'"))
guard commentLabels.count > 0 else {
XCTFail("No feed items found (no comment labels)")
return
}
// Tap on the first comment label's parent area
let firstCommentLabel = commentLabels.element(boundBy: 0)
// Wait for feed to load by waiting for the first comment label to appear
let commentLabels = app.staticTexts.matching(NSPredicate(format: "label CONTAINS '评论'"))
let firstCommentLabel = commentLabels.element(boundBy: 0)
XCTAssertTrue(firstCommentLabel.waitForExistence(timeout: 10), "Feed should load and show at least one comment label")
// Find a feed item by looking for text that says "评论" (comment count)
guard commentLabels.count > 0 else {
XCTFail("No feed items found (no comment labels)")
return
}
// Tap on the first comment label's parent area

Copilot uses AI. Check for mistakes.
Comment on lines 54 to 60
// Wait for navigation animation
sleep(3)

// Verify we're on detail page - look for "话题" text or "发表回复" placeholder
let topicLabel = app.staticTexts["话题"]
let replyPlaceholder = app.textViews.firstMatch

Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace sleep(3) with waitForExistence(timeout:) on the expected detail page element to make the test more reliable and faster.

Suggested change
// Wait for navigation animation
sleep(3)
// Verify we're on detail page - look for "话题" text or "发表回复" placeholder
let topicLabel = app.staticTexts["话题"]
let replyPlaceholder = app.textViews.firstMatch
// Wait for navigation animation by waiting for detail page element to appear
let topicLabel = app.staticTexts["话题"]
let replyPlaceholder = app.textViews.firstMatch
let appeared = topicLabel.waitForExistence(timeout: 5) || replyPlaceholder.waitForExistence(timeout: 5)
XCTAssertTrue(appeared, "Detail page did not appear after tapping comment label")
// Verify we're on detail page - look for "话题" text or "发表回复" placeholder

Copilot uses AI. Check for mistakes.
XCTAssertTrue(onDetailPage, "Should be on detail page after navigation")

// Wait longer to verify page stays open (the original bug was page dismissing)
sleep(3)
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace sleep(3) with a proper assertion mechanism. If testing that the page stays open, consider using XCTWaiter to wait a specific time then assert, or verify the element remains accessible.

Suggested change
sleep(3)
let expectation = XCTestExpectation(description: "Wait 3 seconds to verify detail page stays open")
let result = XCTWaiter.wait(for: [expectation], timeout: 3.0)

Copilot uses AI. Check for mistakes.
XCTAssertTrue(stillOnDetail, "Detail page should remain open after 3 seconds")

// Leave app open for visual inspection
sleep(10)
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this sleep(10) for visual inspection. UI tests should be automated and not require manual inspection during execution. If manual testing is needed, do it separately from automated tests.

Suggested change
sleep(10)
// Removed sleep(10) for automation; UI tests should not require manual inspection.

Copilot uses AI. Check for mistakes.
NodeView(id: data.nodeId.safe, name: data.nodeName.safe)
Text(data.nodeName.safe)
.font(.footnote)
.foregroundColor(.gray)
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replacing NodeView with plain Text removes the navigation functionality to TagDetailPage. This breaks the ability to navigate to tag details from recent items. If this is intentional due to navigation conflicts, consider restoring this functionality once the navigation architecture is stable, or document why this navigation was removed.

Suggested change
.foregroundColor(.gray)
.foregroundColor(.gray)
.to {
TagDetailPage(nodeName: data.nodeName.safe)
}

Copilot uses AI. Check for mistakes.
NavigationLink(destination: UserDetailPage(userId: initData?.id ?? .empty)) {
AvatarView(url: state.model.headerInfo?.avatar ?? .empty, size: 32)
}
AvatarView(url: state.model.headerInfo?.avatar ?? .empty, size: 32)
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing the NavigationLink to UserDetailPage removes the ability to navigate to user profiles from the detail page. This functionality should be restored once navigation issues are resolved, or alternative navigation should be provided (e.g., using NavigationStack's navigation state).

Copilot uses AI. Check for mistakes.
Comment on lines +27 to +29
Text(data.nodeName.safe)
.font(.footnote)
.foregroundColor(.gray)
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replacing NodeView with plain Text removes the navigation functionality to TagDetailPage. Users can no longer tap on node names to navigate to tag details from feed items. Consider restoring this functionality with proper NavigationStack-compatible navigation.

Copilot uses AI. Check for mistakes.
Comment on lines 18 to 20
var isSelected: Bool {
let selected = selecedTab == .feed
if selected && !state.hasLoadedOnce {
dispatch(FeedActions.FetchData.Start(autoLoad: true))
}
return selected
selecedTab == .feed
}
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The removed logic in isSelected that dispatched FetchData.Start when the tab was selected is now duplicated in the onAppear handler (lines 24-28). However, isSelected is a computed property that may be called multiple times. Consider if the original pattern was intentional to trigger fetches on tab selection separately from view appearance. Check if ExplorePage and MessagePage follow similar patterns and ensure consistency.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link

github-actions bot commented Dec 1, 2025

Code Coverage Report ❌

Current coverage: 19.02%

Addresses Copilot review comments:
- Replace sleep() with waitForExistence() for element detection
- Use XCTWaiter for timed assertions instead of sleep
- Remove unnecessary sleep(10) for visual inspection

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@github-actions github-actions bot added size/M and removed size/M labels Dec 1, 2025
@graycreate graycreate merged commit abf298d into main Dec 1, 2025
6 checks passed
@graycreate graycreate deleted the bugfix/navigation-detail-page-closes branch December 1, 2025 10:02
@github-actions
Copy link

github-actions bot commented Dec 1, 2025

Code Coverage Report ❌

Current coverage: 29.06%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants